#include "poller.h"
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

CPollerObject::CPollerObject() : netfd(-1), epoll_fd(-1), events(0) {
}

CPollerObject::~CPollerObject() {
    if (epoll_fd != -1 && netfd != -1) {
        DetachPoller(epoll_fd);
    }
}

int CPollerObject::AttachPoller(int epoll_fd) {
    this->epoll_fd = epoll_fd;
    struct epoll_event ev;
    ev.events = EPOLLIN | EPOLLOUT | EPOLLET; // Edge-triggered
    ev.data.ptr = this;
    
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, netfd, &ev) < 0) {
        return ERR_POLL;
    }
    
    events = ev.events;
    return ERR_SUCCESS;
}

int CPollerObject::DetachPoller(int epoll_fd) {
    if (netfd == -1) return ERR_SUCCESS;
    
    if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, netfd, nullptr) < 0) {
        return ERR_POLL;
    }
    
    epoll_fd = -1;
    events = 0;
    return ERR_SUCCESS;
}

int CPollerObject::EnableInput() {
    events |= EPOLLIN;
    return UpdateEvents();
}

int CPollerObject::EnableOutput() {
    events |= EPOLLOUT;
    return UpdateEvents();
}

int CPollerObject::DisableInput() {
    events &= ~EPOLLIN;
    return UpdateEvents();
}

int CPollerObject::DisableOutput() {
    events &= ~EPOLLOUT;
    return UpdateEvents();
}

int CPollerObject::UpdateEvents() {
    if (epoll_fd == -1 || netfd == -1) return ERR_SUCCESS;
    
    struct epoll_event ev;
    ev.events = events;
    ev.data.ptr = this;
    
    if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, netfd, &ev) < 0) {
        return ERR_POLL;
    }
    
    return ERR_SUCCESS;
}

CPoller::CPoller() : epoll_fd(-1), initialized(false) {
    events.resize(1024);
}

CPoller::~CPoller() {
    if (epoll_fd != -1) {
        close(epoll_fd);
    }
}

int CPoller::Init() {
    epoll_fd = epoll_create1(0);
    if (epoll_fd < 0) {
        return ERR_POLL;
    }
    
    initialized = true;
    return ERR_SUCCESS;
}

int CPoller::AddObject(CPollerObject* obj) {
    if (!initialized) return ERR_POLL;
    return obj->AttachPoller(epoll_fd);
}

int CPoller::RemoveObject(CPollerObject* obj) {
    if (!initialized) return ERR_POLL;
    return obj->DetachPoller(epoll_fd);
}

int CPoller::WaitEvents(int timeout_ms) {
    if (!initialized) return ERR_POLL;
    
    int nfds = epoll_wait(epoll_fd, events.data(), events.size(), timeout_ms);
    if (nfds < 0) {
        if (errno == EINTR) return 0; // Interrupted by signal
        return ERR_POLL;
    }
    
    return nfds;
}

void CPoller::ProcessEvents() {
    int nfds = WaitEvents();
    if (nfds <= 0) return;
    
    for (int i = 0; i < nfds; i++) {
        CPollerObject* obj = static_cast<CPollerObject*>(events[i].data.ptr);
        if (!obj) continue;
        
        uint32_t revents = events[i].events;
        if (revents & (EPOLLERR | EPOLLHUP)) {
            obj->HangupNotify();
        } else {
            if (revents & EPOLLIN) {
                obj->InputNotify();
            }
            if (revents & EPOLLOUT) {
                obj->OutputNotify();
            }
        }
    }
}

CPoller* g_poller = nullptr;